package steveshrader.budget.client.events;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import steveshrader.budget.shared.ExpenseProxy;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;

/**
 * An event to indicate a change in the expense list and that a new summary should be created.
 */
public class ExpensesChangeEvent extends GwtEvent<ExpensesChangeEvent.Handler> {
  /**
   * Handles {@link SummaryChangeEvent}.
   */
  public interface Handler extends EventHandler {
    void onExpensesChanged(ExpensesChangeEvent e);
  }

  public static final Type<Handler> TYPE = new Type<Handler>();

  public static HandlerRegistration register(EventBus eventBus, Handler handler) {
    return eventBus.addHandler(TYPE, handler);
  }

  private final List<ExpenseProxy> expenses;
  private List<ExpenseSummaryEntry> summariesList = new ArrayList<ExpenseSummaryEntry>();

  private long total = 0;	//the total of all expenses in the summary
  
  public ExpensesChangeEvent(List<ExpenseProxy> expenses) {
    this.expenses = expenses;
    calculateSummary();
  }

  private void calculateSummary() {
	summariesList = new ArrayList<ExpenseSummaryEntry>();
	Map<String, Long> summaries = new HashMap<String, Long>();

	total = 0;
	if(expenses != null) {
		for(ExpenseProxy expense : expenses) {
			String expenseType = expense.getExpenseType();
			long currentAmount = summaries.get(expenseType) == null ? 0 : summaries.get(expenseType);
			long expenseAmount = expense.getAmount() == null ? 0 : expense.getAmount();

			currentAmount = currentAmount + expenseAmount;
			
			total = total + expenseAmount;
			summaries.put(expenseType, currentAmount);
		}
	}
	
	summariesList.add(0, new ExpenseSummaryEntry("Total", total));
	order: for (String expenseType : summaries.keySet()) {	//put in desc order by amount
		Long amount = summaries.get(expenseType);
		int index = 0;
		for(ExpenseSummaryEntry expenseSummaryEntry : summariesList) {
			if(amount > expenseSummaryEntry.getAmount()) {
				summariesList.add(index, new ExpenseSummaryEntry(expenseType, amount));
				continue order;
			}
			index++;
		}
		summariesList.add(index, new ExpenseSummaryEntry(expenseType, amount));
	}
  }
  
  @Override
  public Type<Handler> getAssociatedType() {
    return TYPE;
  }

  public List<ExpenseProxy> getExpenses() {
	return expenses;  
  }
  
  /**
   * Sort the map according to value desc, put the Total at the top
   * and return as List<Map.Entry<String, Long>> so that it can be
   * used directly by a CellTable
   * @return
   */
  public List<ExpenseSummaryEntry> getSummaries() {
	return summariesList;
  }

  @Override
  protected void dispatch(Handler handler) {
    handler.onExpensesChanged(this);
  }
  
  public class ExpenseSummaryEntry {
	  String category;
	  Long amount;
	  
	  public ExpenseSummaryEntry(String category, Long amount) {
		  this.category = category;
		  this.amount = amount;
	  }
	  
	  public String getCategory() {
		  return category;
	  }
	  
	  public Long getAmount() {
		  return amount;
	  }
  }
}
